package com.yugao.fintech.draper.admin.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.lang.Opt;
import com.baomidou.mybatisplus.core.toolkit.CollectionUtils;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.yugao.fintech.draper.admin.convert.DeptConvert;
import com.yugao.fintech.draper.admin.entity.SysDept;
import com.yugao.fintech.draper.admin.mapper.DeptMapper;
import com.yugao.fintech.draper.admin.param.DeptVO;
import com.yugao.fintech.draper.admin.param.DeptDTO;
import com.yugao.fintech.draper.admin.service.DeptService;
import com.yugao.fintech.draper.datascope.annotation.DataScope;
import com.yugao.fintech.draper.core.exception.BaseException;
import com.yugao.fintech.draper.security.SecurityUser;
import com.yugao.fintech.draper.security.util.SecurityUserUtil;
import lombok.SneakyThrows;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
import java.util.Objects;

import static com.yugao.fintech.draper.core.constant.RedisKeyConstants.CACHE_PREFIX_DEPT;
import static com.yugao.fintech.draper.core.constant.RedisKeyConstants.CACHE_PREFIX_DEPT_TREE;

@Service
public class DeptServiceImpl implements DeptService {
    @Autowired
    private DeptMapper deptMapper;

    /**
     * 获取部门树
     */
    @Cacheable(value = CACHE_PREFIX_DEPT, key = CACHE_PREFIX_DEPT_TREE, unless = "#result.data==null")
    @Override
    public DeptDTO getDeptTree() {
        return DeptConvert.INSTANCE.convert(deptMapper.getDeptTree());
    }

    /**
     * 根据id删除部门
     */
    @SneakyThrows
    @Transactional(rollbackFor = Exception.class)
    @CacheEvict(value = CACHE_PREFIX_DEPT, allEntries = true)
    @Override
    public Boolean deleteById(Long id) throws BaseException {
        // 判断是否存在下级部门
        Boolean exists = this.existsSubordinate(id);
        if (exists) {
            throw new BaseException("存在子部门,无法删除！");
        }
        // 删除部门
        return deptMapper.deleteById(id) > 0;
    }

    /**
     * 保存部门信息
     */
    @CacheEvict(value = CACHE_PREFIX_DEPT, allEntries = true)
    @Override
    public Boolean addDept(DeptDTO deptDTO) {
        return deptMapper.insert(DeptConvert.INSTANCE.convert(deptDTO)) > 0;
    }

    @Cacheable(value = CACHE_PREFIX_DEPT, key = "#id", unless = "#result.data==null")
    @Override
    public DeptDTO finBydDeptId(Long id) {
        return DeptConvert.INSTANCE.convert(deptMapper.selectById(id));
    }

    @CacheEvict(value = CACHE_PREFIX_DEPT, allEntries = true)
    @Override
    public Boolean updateById(DeptDTO deptDTO) {
        return deptMapper.updateById(DeptConvert.INSTANCE.convert(deptDTO)) > 0;
    }

    /**
     * 获取当前用户部门(包含父级)
     *
     * @return 用户所处部门
     */
    @Override
    public String getDeptNameByUserId() {
        SecurityUser user = Opt.of(SecurityUserUtil.getCurrentUser()).get();
        return deptMapper.getDeptNameByUserId(user.getDeptId());
    }

    /**
     * 根据Pid查询下级部门 避免数据权限查询时循环调用
     */
    @DataScope(enable = false)
    @Cacheable(value = CACHE_PREFIX_DEPT, key = "#pId", unless = "#result==null")
    @Override
    public List<DeptDTO> listDeptsByParentId(Long pId) {
        DeptVO deptVO = deptMapper.listDeptsByParentId(pId);
        return DeptConvert.INSTANCE.convert(treeToList(deptVO));
    }

    /**
     * 当前部门是否存在下级部门
     *
     * @param id 部门id
     * @return true表示存在下级部门 false表示不存在下级部门
     */
    private Boolean existsSubordinate(Long id) {
        SysDept sysDept = deptMapper.selectOne(Wrappers.<SysDept>lambdaQuery().eq(SysDept::getParentId, id).last("limit 1"));
        return Objects.nonNull(sysDept);
    }

    private List<DeptVO> treeToList(DeptVO deptVO) {
        if (Objects.isNull(deptVO)) {
            return new ArrayList<>(0);
        }
        LinkedList<DeptVO> result = new LinkedList<>();
        result.add(deptVO);
        List<DeptVO> children = deptVO.getChildren();
        if (CollectionUtil.isEmpty(children)) {
            return result;
        }
        children.forEach(item -> {
            List<DeptVO> boList = treeToList(item);
            if (CollectionUtils.isNotEmpty(boList)) {
                result.addAll(boList);
            }
        });
        return result;
    }
}
